package com.tuling.jucdemo.jmm;

import com.tuling.jucdemo.factory.UnsafeFactory;

import java.util.concurrent.locks.LockSupport;

/**
 * 可见性问题深入分析
 * -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -Xcomp
 * hsdis-amd64.dll
 */
public class VisibilityTest {
    // storeLoad JVM内存屏障 ------> (汇编层面指令) lock; addl $0,0(%%rsp)
    // lock前缀指令不是内存屏障的指令 但是有内存屏障的效果  缓存失效
    //private volatile boolean flag = true;
    private volatile boolean flag = true;
    //private volatile int count = 0;
    //private Integer count = 0;
    private int count = 0;

    public void refresh(){
        // thread2对flag的写操作会 happens-before  thread1对flag的读操作
        flag = false;
        System.out.println(Thread.currentThread().getName()+"修改flag:"+flag);
    }

    public void load(){
        System.out.println(Thread.currentThread().getName()+"开始执行......");
        while (flag){
            //TODO 业务逻辑
            count++;
            //JMM(Java Memory Model)模型  内存模型: 线程间通信有关  共享内存模型
            //可见性方式如下
            //1.flag变量前加volatile关键字 可以跳出循环 storeLoad
            // JVM内存屏障(汇编层面指令)  lock; addl $0, $0(%rsp)
            // lock前缀指令不是内存屏障的指令,但是有内存屏障的效果 缓存失效  通过内存屏障实现的
            //2.UnsafeFactory.getUnsafe().storeFence(); 内存屏障 也可也可以跳出循环
            //UnsafeFactory.getUnsafe().storeFence();
            //3.Thread.yield(); 可以跳出循环  释放时间片 上下文切换  加载上下文 flag=true
            //4.System.out.println(count); println底层由synchronized实现 也是内存屏障 也可以跳出循环
            //System.out.println(count);
            //5.LockSupport.unpark(Thread.currentThread()); 可以跳出循环
            //LockSupport.unpark(Thread.currentThread());
            //6.count变量前加volatile关键字 可以跳出循环
            //7.private Integer count = 0; 可以跳出循环

            //8.showWaite(1000000);//1ms  缓存数据被清理淘汰了(过期了)      可以跳出循环
            //showWaite(1000000);

//            long start = System.nanoTime();
//            long end = 0;
//            // do while不能跳出循环  while可以跳出循环
//            while(1000000 + start >= end){
//                end = System.nanoTime();
//            }

            //showWaite(1000); //1微秒  缓存数据还在不会淘汰 时间太短 不可以跳出循环
            //Thread.sleep()也是通过内存屏障
//            try {
//                Thread.sleep(1000);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }

            // 总结： java中可见性如何保证？ 方式归类有两种：
            //1.jvm层面 storeLoad内存屏障  ===> x86  lock替代了mfence
            //2.上下文切换 Thread.yield();

            // java
            // valotile 锁机制
            //当前线程对共享变量的操作会存在读不到，或者不能立即读到另一个线程对此变量的写操作

            //lock 硬件层面扩展   JMM为什么选择共享内存模型


        }
        System.out.println(Thread.currentThread().getName()+"跳出循环count:"+count);
    }

    public static void main(String[] args) throws InterruptedException {
        VisibilityTest test = new VisibilityTest();
        Thread t1 = new Thread(()->test.load(),"t1");
        t1.start();

        Thread.sleep(1000);

        Thread t2 = new Thread(()->test.refresh(),"t2");
        t2.start();
    }

    public static void showWaite(long interval){
        long start = System.nanoTime();
        long end;
        do{
            end = System.nanoTime();
        }while(interval + start >= end);
    }
}
